#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
//#include <sys/syscall.h>

#include "DemoMultiThread.h"
#include "DemoDateTime.h"
#include "DemoString.h"

pthread_mutex_t mutA, mutB;

unsigned long long getThreadID(void) {
#ifdef __APPLE__
    __uint64_t tid;
    pthread_t currentThread = pthread_self();
    pthread_threadid_np(currentThread, &tid);
    return (unsigned long long)tid;
#else
    return (unsigned long long)(pthread_self());
#endif
}

void *taskThread(void* args) {
    char* name = args==NULL ? NULL : (char*)args;
    logWs("taskThread, %s, begin", name);
    
    if (0 == strcmp(name, "renwuC")) {
        // C 等待AB完成 后 释放 互斥锁 mutA 和 mutB
        logWs("taskThread, %s, try lock A and B", name);
        pthread_mutex_lock(&mutA);
        pthread_mutex_lock(&mutB);
        logWs("taskThread, %s, succ to lock A and B", name);
    }
    
    char srcStr[1024] = {0};
    
    //pid_t x = syscall(SYS_gettid);
    
    srand((int)(getTsInWeimiao()));     // 每次执行种子不同，生成不同的随机数
    int sleepUS = rand() % 2000000;     // 2s 内的随机数
    usleep(sleepUS);                    // 模拟线程任务耗时
    
    // 组装线程退出返回数据 rtnStr;
    sprintf(srcStr, "tid:%llu, %s, usleep %d", getThreadID(), name, sleepUS);
    char* rtnStr = malloc2Strcpy(srcStr);
    
    if (0 == strcmp(name, "renwuA")) {
        // A完成 后 释放 互斥锁 mutA
        logWs("taskThread, %s, unlock A", name);
        pthread_mutex_unlock(&mutA);
    }
    if (0 == strcmp(name, "renwuB")) {
        // B完成 后 释放 互斥锁 mutB
        logWs("taskThread, %s, unlock B", name);
        pthread_mutex_unlock(&mutB);
    }
    
    logWs("taskThread, %s, end", name);
    
    return rtnStr;
}


void DemoMultiThread(void) {
    logWsBegin("DemoMultiThread begin\n");
    
    // 4个任务ABCD
    // C依赖AB完成
    // D和ABC无关，期望尽量快执行完成所有任务
    
    pthread_t tA, tB, tC, tD;
    
    pthread_mutex_init(&mutA, NULL);
    pthread_mutex_lock(&mutA);
    // 拿到 互斥锁 mutA 后再开始线程A
    pthread_create(&tA, NULL, taskThread, (void *)("renwuA"));
    
    pthread_mutex_init(&mutB, NULL);
    pthread_mutex_lock(&mutB);
    // 拿到 互斥锁 mutB 后再开始线程B
    pthread_create(&tB, NULL, taskThread, (void *)("renwuB"));
    
    // C线程内部, 等待AB完成后释放 互斥锁 mutA 和 mutB，然后再执行
    pthread_create(&tC, NULL, taskThread, (void *)("renwuC"));
    
    pthread_create(&tD, NULL, taskThread, (void *)("renwuD"));
    
    char* threadReturnA = NULL;
    char* threadReturnB = NULL;
    char* threadReturnC = NULL;
    char* threadReturnD = NULL;
    
    pthread_join(tA, (void **)(&threadReturnA));
    pthread_join(tB, (void **)(&threadReturnB));
    pthread_join(tC, (void **)(&threadReturnC));
    pthread_join(tD, (void **)(&threadReturnD));
    
    if (threadReturnA) {
        printf ("\nthreadReturnA: %s\n", threadReturnA);
        free(threadReturnA);
    }
    
    if (threadReturnB) {
        printf ("\nthreadReturnB: %s\n", threadReturnB);
        free(threadReturnB);
    }
    
    if (threadReturnC) {
        printf ("\nthreadReturnC: %s\n", threadReturnC);
        free(threadReturnC);
    }
    
    if (threadReturnD) {
        printf ("\nthreadReturnD: %s\n", threadReturnD);
        free(threadReturnD);
    }
    
    pthread_mutex_destroy(&mutA);
    pthread_mutex_destroy(&mutB);
    
    logWs("DemoMultiThread end\n");
}


void *taskABD(void* args) {
    sem_t* sem = args==NULL ? NULL : (sem_t*)args;
    logWs("taskABD begin, tid=%llu, sem ptr = %p", getThreadID(), sem);
    
    srand((int)(getTsInWeimiao()));     // 每次执行种子不同，生成不同的随机数
    int sleepUS = rand() % 2000000;     // 2s 内的随机数
    usleep(sleepUS);                    // 模拟线程任务耗时
    
    sem_post(sem);
    
    // 最简单的办法，在线程执行结束后调用pthread_detach让他自己释放
    pthread_detach(pthread_self());
    
    logWs("taskABD end, tid=%llu, usleep %d, sem_post %p", getThreadID(), sleepUS, sem);
    return NULL;
}

struct semABC {
    sem_t* semA;
    sem_t* semB;
    sem_t* semC;
};

void *taskC(void* args) {
    struct semABC* sABC = args==NULL ? NULL : (struct semABC*)args;

    logWs("taskC begin, tid=%llu, semA=%p, semB=%p, semC=%p", getThreadID(), sABC->semA, sABC->semB, sABC->semC);
    sem_wait(sABC->semA);
    sem_wait(sABC->semB);
    logWs("taskC after sem wait, tid=%llu", getThreadID());
    
    srand((int)(getTsInWeimiao()));     // 每次执行种子不同，生成不同的随机数
    int sleepUS = rand() % 2000000;     // 2s 内的随机数
    usleep(sleepUS);                    // 模拟线程任务耗时
    
    sem_post(sABC->semC);
    
    // 最简单的办法，在线程执行结束后调用pthread_detach让他自己释放
    pthread_detach(pthread_self());
    
    logWs("taskC end, tid=%llu, usleep %d, sem_post %p", getThreadID(), sleepUS, sABC->semC);
    return NULL;
}

void DemoMultiThreadSemaphore(void) {
    logWsBegin("DemoMultiThreadSemaphore begin\n");
    
    // 4个任务ABCD
    // C依赖AB完成
    // D和ABC无关，期望尽量快执行完成所有任务
    
    sem_t *semA, *semB, *semC, *semD;
    
#ifdef __APPLE__
    // 苹果系统 不能创建 无名信号量，只支持创建有名信号量
    sem_unlink("semA");
    sem_unlink("semB");
    sem_unlink("semC");
    sem_unlink("semD");
    semA = sem_open("semA", O_CREAT|O_EXCL, S_IRWXU, 0);
    if(semA == SEM_FAILED){
        logWs("semA fail to sem_open\n");
        return;
    }
    semB = sem_open("semB", O_CREAT|O_EXCL, S_IRWXU, 0);
    if(semB == SEM_FAILED){
        logWs("semB fail to sem_open\n");
        return;
    }
    semC = sem_open("semC", O_CREAT|O_EXCL, S_IRWXU, 0);
    if(semC == SEM_FAILED){
        logWs("semC fail to sem_open\n");
        return;
    }
    semD = sem_open("semD", O_CREAT|O_EXCL, S_IRWXU, 0);
    if(semD == SEM_FAILED){
        logWs("semD fail to sem_open\n");
        return;
    }
    
#else
    //int sem_init(sem_t *sem, int pshared, unsigned int value); 其中sem是要初始化的信号量，pshared不为０时此信号量在进程间共享，为0时当前进程的所有线程共享，value是信号量的初始值。
    if (-1 == sem_init(semA, 0, 0)) {
        logWs("semA fail to init\n");
        return;
    }
    if (-1 == sem_init(semB, 0, 0)) {
        logWs("semB fail to init\n");
        return;
    }
    if (-1 == sem_init(semC, 0, 0)) {
        logWs("semC fail to init\n");
        return;
    }
    if (-1 == sem_init(semD, 0, 0)) {
        logWs("semD fail to init\n");
        return;
    }

#endif
    
    struct semABC sABC;
    sABC.semA = semA;
    sABC.semB = semB;
    sABC.semC = semC;
    
    //logWs("befor pthread_create, %llu, semA=%d, semB=%d, semC=%d", getThreadID(), *semA, *semB, *semC);
    
    // DETACHED 模式创建 线程
    pthread_t tA, tB, tC, tD;
    
    pthread_attr_t attr;
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
    
    pthread_create(&tA, &attr, taskABD, (void *)(semA));
    pthread_create(&tB, &attr, taskABD, (void *)(semB));
    pthread_create(&tC, &attr, taskC,   (void *)(&sABC));
    pthread_create(&tD, &attr, taskABD, (void *)(semD));
    
    logWs("after pthread_create, %llu", getThreadID());
    
    //sem_wait(&semA);
    //sem_wait(&semB);
    //int sem_wait(sem_t *sem); 等待信号量，如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
    sem_wait(semC);
    sem_wait(semD);


#ifdef __APPLE__
    sem_close(semA);sem_unlink("semA");
    sem_close(semB);sem_unlink("semB");
    sem_close(semC);sem_unlink("semC");
    sem_close(semD);sem_unlink("semD");
#else
    sem_destroy(semA);//只有用sem_init初始化的信号量才能用sem_destroy销毁。
    sem_destroy(semB);
    sem_destroy(semC);
    sem_destroy(semD);
#endif
    pthread_attr_destroy(&attr);
    
    logWs("DemoMultiThreadSemaphore end\n");
}
